#include "com_sparse_row.h"
#include <cmath>

SMatrix::SMatrix() {
  /*
          构造函数，初始化
  */
  this->row_num = 0; //原矩阵行数
  this->col_num = 0; //原矩阵列数
  this->tot_num = 0; //原矩阵非零元素个数
}

void SMatrix::build(vector<vector<int>> mat) {
  flag_int_double = 0;
  this->vec_I.clear();
  this->col_indices.clear();
  this->row_offsets.clear();
  int row_size = mat.size();
  if (row_size == 0) {
    cout << "Wrong happened here SMatrix::SMatrix row_size == 0" << endl;
    exit(0);
  }
  this->row_num = row_size;
  int col_size = mat[0].size();
  this->col_num = col_size;

  //int row_flag = -1;
  int tot_num_t = 0;

  for (int i = 0; i < row_size; i++) {
    this->row_offsets.push_back(tot_num_t);
    for (int j = 0; j < col_size; j++) {
      if (std::abs(mat[i][j]) > 0.0) {
        tot_num_t++;
        int val = mat[i][j];
        this->vec_I.push_back(val);
        this->col_indices.push_back(j);
      }
    }
  }
  this->row_offsets.push_back(tot_num_t);
  this->tot_num = tot_num_t;
}
void SMatrix::build(vector<vector<double>> mat) {
  flag_int_double = 1;
  this->vec_D.clear();
  this->col_indices.clear();
  this->row_offsets.clear();
  int row_size = mat.size();
  if (row_size == 0) {
    cout << "Wrong happened here SMatrix::SMatrix row_size == 0" << endl;
    exit(0);
  }
  this->row_num = row_size;
  int col_size = mat[0].size();
  this->col_num = col_size;

  //int row_flag = -1;
  int tot_num_t = 0;

  for (int i = 0; i < row_size; i++) {
    this->row_offsets.push_back(tot_num_t);
    for (int j = 0; j < col_size; j++) {
      if (std::abs(mat[i][j]) > 0.0) {
        tot_num_t++;
        double val = mat[i][j];
        this->vec_D.push_back(val);
        this->col_indices.push_back(j);
      }
    }
  }
  this->row_offsets.push_back(tot_num_t);
  this->tot_num = tot_num_t;
}

int SMatrix::build(vector<vector<int>> mat, vector<int> &row_off,
                   vector<int> &col_ind, vector<int> &vec) {
  row_off.clear();
  col_ind.clear();
  vec.clear();
  int row_size = mat.size();
  if (row_size == 0) {
    cout << "Wrong happened here SMatrix::SMatrix row_size == 0" << endl;
    exit(0);
  }
  // this->row_num = row_size;
  int col_size = mat[0].size();
  // this->col_num = col_size;

  //int row_flag = -1;
  int tot_num_t = 0;

  for (int i = 0; i < row_size; i++) {
    row_off.push_back(tot_num_t);
    for (int j = 0; j < col_size; j++) {
      if (std::abs(mat[i][j]) > 0.0) {
        tot_num_t++;
        int val = mat[i][j];
        vec.push_back(val);
        col_ind.push_back(j);
      }
    }
  }
  row_off.push_back(tot_num_t);
  // this->tot_num = tot_num_t;
  return tot_num_t;
}

int SMatrix::build(vector<vector<double>> mat, vector<int> &row_off,
                   vector<int> &col_ind, vector<double> &vec) {
  row_off.clear();
  col_ind.clear();
  vec.clear();
  int row_size = mat.size();
  if (row_size == 0) {
    cout << "Wrong happened here SMatrix::SMatrix row_size == 0" << endl;
    exit(0);
  }
  // this->row_num = row_size;
  int col_size = mat[0].size();
  // this->col_num = col_size;

  //int row_flag = -1;
  int tot_num_t = 0;

  for (int i = 0; i < row_size; i++) {
    row_off.push_back(tot_num_t);
    for (int j = 0; j < col_size; j++) {
      if (std::abs(mat[i][j]) > 0.0) {
        tot_num_t++;
        double val = mat[i][j];
        vec.push_back(val);
        col_ind.push_back(j);
      }
    }
  }
  row_off.push_back(tot_num_t);
  // this->tot_num = tot_num_t;
  return tot_num_t;
}

double SMatrix::Access(int row, int col) {
  if (row >= row_num || col >= col_num) {
    cout << "Wrong happened @ SMatrix::Acess" << endl;
    exit(0);
  }

  int col_beg = this->row_offsets[row];
  int col_end = this->row_offsets[row + 1];

  if (flag_int_double == 0) {
    int val = 0;
    for (int i = col_beg; i < col_end; i++) {
      if (this->col_indices[i] == col) {
        val = this->vec_I[i];
        break;
      }
    }
    return val;
  } else if (flag_int_double == 1) {
    double val = 0;
    for (int i = col_beg; i < col_end; i++) {
      if (this->col_indices[i] == col) {
        val = this->vec_D[i];
        break;
      }
    }
    return val;
  } else {
    cout << "Wrong happened @ SMatrix::Access" << endl;
    exit(0);
  }
  // cout<<"row : "<<row<<"\t col : "<<col<<"\t val : "<<val<<endl;
}
// double SMatrix::Access(int row_size, int col_size, vector<int> &row_off,
//                        vector<int> &col_ind, vector<double> &vec, int row,
//                        int col) {
//   if (row >= row_size || col >= col_size) {
//     cout << "Wrong happened @ SMatrix::Acess" << endl;
//     exit(0);
//   }
//   double val = 0.0;
//
//   int col_beg = row_off[row];
//   int col_end = row_off[row + 1];
//   for (int i = col_beg; i < col_end; i++) {
//     if (col_ind[i] == col) {
//       val = vec[i];
//       break;
//     }
//   }
//   return val;
// }

void SMatrix::Rebuild(vector<vector<int>> &mat_R) {
  mat_R.resize(row_num);
  for (int i = 0; i < row_num; i++) {
    mat_R[i].resize(col_num);
    for (int j = 0; j < col_num; j++) {
      mat_R[i][j] = 0; // this->Access(i, j);
      // cout << i << "\t " << j << "\t" << mat_R[i][j] << endl;
    }
    int x_beg = row_offsets[i];
    int x_end = row_offsets[i + 1];
    for (int x = x_beg; x < x_end; x++) {
      int j = col_indices[x];
      int val = vec_I[x];
      mat_R[i][j] = val;
    }
  }
}

void SMatrix::Rebuild(vector<vector<double>> &mat_R) {
  mat_R.resize(row_num);
  for (int i = 0; i < row_num; i++) {
    mat_R[i].resize(col_num);
    for (int j = 0; j < col_num; j++) {
      mat_R[i][j] = 0; // this->Access(i, j);
      // cout << i << "\t " << j << "\t" << mat_R[i][j] << endl;
    }
    int x_beg = row_offsets[i];
    int x_end = row_offsets[i + 1];
    for (int x = x_beg; x < x_end; x++) {
      int j = col_indices[x];
      double val = vec_D[x];
      mat_R[i][j] = val;
    }
  }
}

void SMatrix::check_Two_mat(vector<vector<double>> mat,
                            vector<vector<double>> mat_R) {
  int size = mat.size();
  if (size != mat_R.size()) {
    cout << "Wrong happened @ check_Two_mat 1 " << endl;
    cout << "mat.size() : " << mat.size()
         << "\t mat_R.size() : " << mat_R.size() << endl;
    exit(0);
  }
  for (int i = 0; i < size; i++) {
    int size_j = mat[i].size();
    if (size_j != mat_R[i].size()) {
      cout << "Wrong happened @ check_Two_mat 2" << endl;
      exit(0);
    }
    for (int j = 0; j < size_j; j++) {
      double val = mat[i][j];
      double val_R = mat_R[i][j];
      double val_A = std::abs(val);
      double val_RA = std::abs(val_R);
      double diff = val_R - val;
      if (std::abs(diff) > 0.00000001) {
        // if (val_A > 0.0 || val_RA > 0.0) {
        cout << "i : " << i << "\t j : " << j << "\t val = " << val
             << "\t val_R = " << val_R << endl;
        exit(0);
      }
    }
  }
}

void SMatrix::clear() {
  this->row_offsets.clear();
  this->col_indices.clear();
  this->vec_D.clear();
  this->vec_I.clear();

} // ~~~~~~~~~~~~~~~~~~~~~~~ SMatrix_Ch::SMatrix_Ch()  ~~~~~~~~~~~~~~~~~~~~~~ //
SMatrix_Ch::SMatrix_Ch() {
  /*
          构造函数，初始化
  */
  this->row_num = 0; //原矩阵行数
  this->col_num = 0; //原矩阵列数
  this->tot_num = 0; //原矩阵非零元素个数
}

void SMatrix_Ch::init_ch(int size_ch_t, int type) {
  if (size_ch_t == 0) {
    cout << "Wrong happened here @ SMatrix_Ch::SMatrix_Ch  this->size_ch == 0"
         << endl;
    exit(0);
  }

  this->size_ch = size_ch_t;
  this->row_num_ch.clear();
  this->col_num_ch.clear();
  this->tot_num_ch.clear();
  this->row_off_ch.clear();
  this->col_ind_ch.clear();
  this->vec_I_ch.clear();
  this->vec_D_ch.clear();

  this->row_num_ch.resize(size_ch);
  this->col_num_ch.resize(size_ch);
  this->tot_num_ch.resize(size_ch);
  this->row_off_ch.resize(size_ch);
  this->col_ind_ch.resize(size_ch);
  if (type == 0)
    this->vec_I_ch.resize(size_ch);
  if (type == 1)
    this->vec_D_ch.resize(size_ch);
  Flag_init_ch = 1;
  return;
}

void SMatrix_Ch::build_ch(vector<vector<int>> &mat_ch_sep, int ch) {
  this->flag_int_double_ch = 0;
  if (Flag_init_ch < 0) {
    cout << "Wront @ SMatrix_Ch::build_ch 1 Flag_init_ch < 0 " << endl;
    cout << " init_ch(int size_ch) First ! " << endl;
    exit(0);
  }
  if (ch >= this->size_ch) {
    cout << "Wront @ SMatrix_Ch::build_ch 1" << endl;
    cout << " this->size_ch = " << this->size_ch << "\t ch :  " << ch << endl;
    exit(0);
  }
  vector<int> row_off_t;
  vector<int> col_ind_t;
  vector<int> vec_t;
  int nonzero_num = this->build(mat_ch_sep, row_off_t, col_ind_t, vec_t);
  this->row_off_ch[ch] = row_off_t;
  this->col_ind_ch[ch] = col_ind_t;
  this->vec_I_ch[ch] = vec_t;

  int size_row = mat_ch_sep.size();
  int size_col = 0;
  if (size_row > 0)
    size_col = mat_ch_sep[0].size();
  row_num_ch[ch] = size_row;
  col_num_ch[ch] = size_col;
  tot_num_ch[ch] = nonzero_num;
}

void SMatrix_Ch::build_ch(vector<vector<double>> &mat_ch_sep, int ch) {
  this->flag_int_double_ch = 1;
  if (Flag_init_ch < 0) {
    cout << "Wront @ SMatrix_Ch::build_ch 2 Flag_init_ch < 0 " << endl;
    cout << " init_ch(int size_ch) First ! " << endl;
    exit(0);
  }
  if (ch >= this->size_ch) {
    cout << "Wront @ SMatrix_Ch::build_ch 2 " << endl;
    cout << " this->size_ch = " << this->size_ch << "\t ch :  " << ch << endl;
    exit(0);
  }
  vector<int> row_off_t;
  vector<int> col_ind_t;
  vector<double> vec_t;
  int nonzero_num = this->build(mat_ch_sep, row_off_t, col_ind_t, vec_t);
  this->row_off_ch[ch] = row_off_t;
  this->col_ind_ch[ch] = col_ind_t;
  this->vec_D_ch[ch] = vec_t;

  int size_row = mat_ch_sep.size();
  int size_col = 0;
  if (size_row > 0)
    size_col = mat_ch_sep[0].size();
  row_num_ch[ch] = size_row;
  col_num_ch[ch] = size_col;
  tot_num_ch[ch] = nonzero_num;
}

void SMatrix_Ch::build_ch(vector<vector<vector<int>>> &mat_ch) {
  this->flag_int_double_ch = 0;
  this->size_ch = mat_ch.size();
  if (size_ch == 0) {
    cout << "Wrong happened here @ SMatrix_Ch::SMatrix_Ch  this->size_ch == 0"
         << endl;
    exit(0);
  }

  this->row_num_ch.clear();
  this->col_num_ch.clear();
  this->tot_num_ch.clear();
  this->row_off_ch.clear();
  this->col_ind_ch.clear();
  this->vec_I_ch.clear();

  this->row_num_ch.resize(size_ch);
  this->col_num_ch.resize(size_ch);
  this->tot_num_ch.resize(size_ch);

  this->row_off_ch.resize(size_ch);
  this->col_ind_ch.resize(size_ch);
  this->vec_I_ch.resize(size_ch);
  for (int ch = 0; ch < size_ch; ch++) {

    vector<int> row_off_t;
    vector<int> col_ind_t;
    vector<int> vec_t;
    int nonzero_num = this->build(mat_ch[ch], row_off_t, col_ind_t, vec_t);
    this->row_off_ch[ch] = row_off_t;
    this->col_ind_ch[ch] = col_ind_t;
    this->vec_I_ch[ch] = vec_t;

    int size_row = mat_ch[ch].size();
    int size_col = 0;
    if (size_row > 0)
      size_col = mat_ch[ch][0].size();
    this->row_num_ch[ch] = size_row;
    this->col_num_ch[ch] = size_col;
    this->tot_num_ch[ch] = nonzero_num;
  }
}

void SMatrix_Ch::build_ch(vector<vector<vector<double>>> &mat_ch) {
  this->flag_int_double_ch = 1;
  this->size_ch = mat_ch.size();
  if (size_ch == 0) {
    cout << "Wrong happened here @ SMatrix_Ch::SMatrix_Ch  this->size_ch == 0"
         << endl;
    exit(0);
  }
  this->row_num_ch.resize(size_ch);
  this->col_num_ch.resize(size_ch);
  this->tot_num_ch.resize(size_ch);

  this->row_off_ch.resize(size_ch);
  this->col_ind_ch.resize(size_ch);
  this->vec_D_ch.resize(size_ch);
  for (int ch = 0; ch < size_ch; ch++) {

    vector<int> row_off_t;
    vector<int> col_ind_t;
    vector<double> vec_t;
    int nonzero_num = this->build(mat_ch[ch], row_off_t, col_ind_t, vec_t);
    this->row_off_ch[ch] = row_off_t;
    this->col_ind_ch[ch] = col_ind_t;
    this->vec_D_ch[ch] = vec_t;

    int size_row = mat_ch[ch].size();
    int size_col = 0;
    if (size_row > 0)
      size_col = mat_ch[ch][0].size();
    row_num_ch[ch] = size_row;
    col_num_ch[ch] = size_col;
    tot_num_ch[ch] = nonzero_num;
  }
}

void SMatrix_Ch::cal_init(int ch_size, int type) {
  // int : type = 0  double : 1
  this->flag_int_double_ch = type;
  this->size_ch = ch_size;
  if (size_ch == 0) {
    cout << "Wrong happened here @ SMatrix_Ch::SMatrix_Ch  this->size_ch == 0"
         << endl;
    exit(0);
  }
  this->row_num_ch.resize(size_ch);
  this->col_num_ch.resize(size_ch);
  this->tot_num_ch.resize(size_ch);

  this->row_off_ch.resize(size_ch);
  this->col_ind_ch.resize(size_ch);
  if (type == 0)
    this->vec_I_ch.resize(size_ch);
  if (type == 1)
    this->vec_D_ch.resize(size_ch);
}

void SMatrix_Ch::cal_beg(int row_size, int col_size) {
  this->tot_num_cal = 0;
  this->row_size_cal = row_size;
  this->col_size_cal = col_size;
  this->row_off_cal.clear();
  this->col_ind_cal.clear();
  this->vec_D_cal.clear();
  this->vec_I_cal.clear();
}
void SMatrix_Ch::cal_row_off(int ch, int i) {
  if (i > this->row_size_cal) {
    cout << "Wrong @ Smatrix_Ch::cal_row_off" << endl;
    cout << "i>this->row_size_cal " << endl;
    exit(0);
  }
  this->row_off_cal.push_back(tot_num_cal);
}
int SMatrix_Ch::cal_col_ind(int ch, int i, int j, double val) {
  if (this->flag_int_double_ch != 1) {
    cout << "Wrong @ SMatrix_Ch::cal_col_ind 2 " << endl;
    exit(0);
  }
  int res = -1;
  if (i > this->row_size_cal || j > this->col_size_cal) {
    cout << "Wrong @ Smatrix_Ch::cal_ing" << endl;
    cout << "i>this->row_size_cal || j>this->col_size_cal" << endl;
    exit(0);
  }
  if (std::abs(val) < this->EPS) {
    return res;
  }
  this->tot_num_cal++;
  this->vec_D_cal.push_back(val);
  this->col_ind_cal.push_back(j);
  return 1;
}
int SMatrix_Ch::cal_col_ind(int ch, int i, int j, int val) {
  if (this->flag_int_double_ch != 0) {
    cout << "Wrong @ SMatrix_Ch::cal_col_ind 3 " << endl;
    exit(0);
  }
  int res = -1;
  if (i > this->row_size_cal || j > this->col_size_cal) {
    cout << "Wrong @ Smatrix_Ch::cal_ing" << endl;
    cout << "i>this->row_size_cal || j>this->col_size_cal" << endl;
    exit(0);
  }
  if (std::abs(val) < this->EPS) {
    return res;
  }
  this->tot_num_cal++;
  this->vec_I_cal.push_back(val);
  this->col_ind_cal.push_back(j);
}
void SMatrix_Ch::cal_end(int ch) {
  this->row_off_cal.push_back(tot_num_cal);
  this->row_off_ch[ch] = row_off_cal;
  this->col_ind_ch[ch] = col_ind_cal;
  if (this->flag_int_double_ch == 1)
    this->vec_D_ch[ch] = vec_D_cal;
  if (this->flag_int_double_ch == 0)
    this->vec_I_ch[ch] = vec_I_cal;
  this->row_num_ch[ch] = row_size_cal;
  this->col_num_ch[ch] = col_size_cal;
  this->tot_num_ch[ch] = tot_num_cal;
}

double SMatrix_Ch::val(int &ch, int &row, int &col) {
  if (ch >= size_ch) {
    cout << "Wrong happened @ SMatrix_Ch::Acess 0" << endl;
    exit(0);
  }
  if (row >= row_num_ch[ch] || col >= col_num_ch[ch]) {
    cout << "Wrong happened @ SMatrix_Ch::Acess" << endl;
    exit(0);
  }

  int col_beg = this->row_off_ch[ch][row];
  int col_end = this->row_off_ch[ch][row + 1];

  if (this->flag_int_double_ch == 0) {
    int val = 0;
    for (int i = col_beg; i < col_end; i++) {
      if (this->col_ind_ch[ch][i] == col) {
        val = this->vec_I_ch[ch][i];
        break;
      }
    }
    return val;
  } else if (this->flag_int_double_ch == 1) {
    double val = 0.0;
    for (int i = col_beg; i < col_end; i++) {
      if (this->col_ind_ch[ch][i] == col) {
        val = this->vec_D_ch[ch][i];
        break;
      }
    }
    return val;
  } else {
    cout << "Wrong happened @ SMatrix_Ch::val 7" << endl;
    exit(0);
  }
  // return val;
}

void SMatrix_Ch::Rebuild(vector<vector<vector<int>>> &mat_R) {
  if (flag_int_double_ch != 0) {
    cout << "Wrong happened @ SMatrix_Ch::Rebuild 1" << endl;
    exit(0);
  }
  mat_R.resize(size_ch);
  for (int ch = 0; ch < size_ch; ch++) {
    int size_i = row_num_ch[ch];
    int size_j = col_num_ch[ch];
    mat_R[ch].resize(size_i);
    for (int i = 0; i < size_i; i++) {
      mat_R[ch][i].resize(size_j);
      for (int j = 0; j < size_j; j++) {
        int val = 0; // this->val(ch, i, j);
        mat_R[ch][i][j] = val;
      }
      int x_beg = row_off_ch[ch][i];
      int x_end = row_off_ch[ch][i + 1];
      for (int x = x_beg; x < x_end; x++) {
        int j = col_ind_ch[ch][x];
        int val = vec_I_ch[ch][x];
        mat_R[ch][i][j] = val;
      }
    }
  }
}

void SMatrix_Ch::Rebuild(vector<vector<vector<double>>> &mat_R) {
  if (flag_int_double_ch != 1) {
    cout << "Wrong happened @ SMatrix_Ch::Rebuild 2" << endl;
    cout << "flag_int_double_ch : " << flag_int_double_ch << endl;
    exit(0);
  }
  mat_R.resize(size_ch);
  for (int ch = 0; ch < size_ch; ch++) {
    int size_i = row_num_ch[ch];
    int size_j = col_num_ch[ch];
    mat_R[ch].resize(size_i);
    for (int i = 0; i < size_i; i++) {
      mat_R[ch][i].resize(size_j);
      for (int j = 0; j < size_j; j++) {
        double val = 0; // this->val(ch, i, j);
        mat_R[ch][i][j] = val;
      }
      int x_beg = row_off_ch[ch][i];
      int x_end = row_off_ch[ch][i + 1];
      for (int x = x_beg; x < x_end; x++) {
        int j = col_ind_ch[ch][x];
        double val = vec_D_ch[ch][x];
        mat_R[ch][i][j] = val;
      }
    }
  }
}

void SMatrix_Ch::check_Two_mat(vector<vector<vector<double>>> &mat,
                               vector<vector<vector<double>>> &mat_R) {
  int size_ch = mat.size();
  if (size_ch != mat_R.size()) {
    cout << "Wrong happened @ SMatrix_Ch::check_Two_mat_ch 1 " << endl;
    cout << "mat.size() : " << mat.size()
         << "\t mat_R.size() : " << mat_R.size() << endl;
    exit(0);
  }
  for (int ch = 0; ch < size_ch; ch++) {
    int size = mat[ch].size();
    for (int i = 0; i < size; i++) {
      int size_j = mat[ch][i].size();
      if (size_j != mat_R[ch][i].size()) {
        cout << "Wrong happened @ SMatrix_Ch::check_Two_mat 2" << endl;
        exit(0);
      }
      for (int j = 0; j < size_j; j++) {
        double val = mat[ch][i][j];
        double val_R = mat_R[ch][i][j];
        double val_A = std::abs(val);
        double val_RA = std::abs(val_R);
        double diff = val_R - val;
        if (std::abs(diff) > 0.0) {
          cout << "ch = " << ch << "i : " << i << "\t j : " << j
               << "\t val = " << val << "\t val_R = " << val_R << endl;
          exit(0);
        }
      }
    }
  }
  cout << "Success : mat == mat_R" << endl;
}

void SMatrix_Ch::clear() {
  this->row_num_ch.clear();
  this->col_num_ch.clear();
  this->tot_num_ch.clear();
  this->row_off_ch.clear();
  this->col_ind_ch.clear();
  this->vec_D_ch.clear();
  this->vec_I_ch.clear();
  this->row_off_cal.clear();
  this->col_ind_cal.clear();
  this->vec_I_cal.clear();
  this->vec_D_cal.clear();
}
